{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit RenderCache;

interface

uses Windows, Canvas, AHMTypes, DirectXSprite, Viewport, Colour;

type
  TAHMRenderCache = class
  private
    FDrawBuffer: TAHMDrawBuffer;
    FCache: TAHMDXSprite;
    FDirty: Boolean;
    FSurfaceHandle: HDC;
  protected
    function GetValid: Boolean;
  public
    constructor Create;
    destructor Destroy; override;
    property Valid: Boolean read GetValid;
    property DrawBuffer: TAHMDrawBuffer read FDrawBuffer write FDrawBuffer;
    property SurfaceHandle: HDC read FSurfaceHandle;
    procedure ClearCache;
    function LockGDICache(Width, Height: Integer): Boolean;
    procedure UnlockGDICache;
    function BuildCache(ScreenRect: TAHMRect): Boolean;
    procedure SaveCache(Filename: String);
    procedure Render(DestRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour; XScale, YScale: Single; XFlip, YFlip: Boolean); overload;
    procedure Render(SrcRect, DestRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour; XScale, YScale: Single; XFlip, YFlip: Boolean); overload;
    procedure RenderClipped(DestRect, ClipRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour; XScale, YScale: Single; XFlip, YFlip: Boolean); overload;
    procedure RenderClipped(SrcRect, DestRect, ClipRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour; XScale, YScale: Single; XFlip, YFlip: Boolean); overload;
  end;

implementation

uses DirectXHelper, Screen, Graphic;

constructor TAHMRenderCache.Create;
begin
  inherited Create;

  // Obtain global drawbuffer for screen
  FDrawBuffer := ScreenInstance.DrawingBuffer;
end;

destructor TAHMRenderCache.Destroy;
begin
  // Ensure we free our cached DX sprite
  FCache.Free;

  inherited Destroy;
end;

function TAHMRenderCache.GetValid: Boolean;
begin
  // Our cache is valid if we have a DX sprite allocated, and it's valid
  Result := Assigned(FCache) and FCache.Available;
end;

procedure TAHMRenderCache.ClearCache;
begin
  // Just erase current contents of cache, but don't free it
  if Assigned(FCache) and FDirty then
  begin
    FCache.ClearTexture;
    FDirty := False;
  end;
end;

function TAHMRenderCache.LockGDICache(Width, Height: Integer): Boolean;
begin
  // Set default result - failed to prepare cache
  Result := False;

  // Don't build cache for zero dimensioned window
  if (Width <= 0) or (Height <= 0) then Exit;

  // Create sprite cache if not already done so
  if not Assigned(FCache) then
  begin
    FCache := DirectXInstance.SpriteManager.CreateSprite(Width, Height);
    FCache.AlphaBlending := False; // Required for GDI render
    FCache.Monochrome := False;
  end;

  // Accomodate any size changes since previous renders
  FCache.SetDimensions(Width, Height);

  // Allocate a GDI window for our texture cache
  Result := FCache.AllocateHandle;
  FSurfaceHandle := FCache.SurfaceHandle;
end;

procedure TAHMRenderCache.UnlockGDICache;
begin
  // Release GDI window handle for our texture cache & mark dirty cache
  FCache.ReleaseHandle;
  FSurfaceHandle := 0;
  FDirty := True;
end;

function TAHMRenderCache.BuildCache(ScreenRect: TAHMRect): Boolean;
var
  LockData: TAHMBitmapData;
begin
  // Set default result - failed to build cache
  Result := False;

  // Don't build cache for zero dimensioned source
  if (ScreenRect.Width <= 0) or (ScreenRect.Height <= 0) then Exit;

  // Ensure we can use global screen drawbuffer
  if not Assigned(FDrawBuffer) then Exit;

  // Create sprite cache if not already done so
  if not Assigned(FCache) then
  begin
    FCache := DirectXInstance.SpriteManager.CreateSprite(
                ScreenRect.Width, ScreenRect.Height);
    FCache.AlphaBlending := FDrawBuffer.AlphaBlending;
    FCache.Monochrome := FDrawBuffer.Monochrome;
  end;

  // Accomodate any size changes since previous renders
  FCache.SetDimensions(ScreenRect.Width, ScreenRect.Height);

  // Lock appropriate memory region for screen drawbuffer
  LockData.LockMode := lmRead;
  if not FDrawBuffer.LockBitmap(ScreenRect, LockData) then Exit;
  try
    // Now copy bitmap into sprite texture
    DirectXInstance.SpriteManager.UpdateSprite(FCache, LockData);
  finally
    FDrawBuffer.UnlockBitmap;
  end;

  // Success - we have now built our cached render
  FDirty := True;
  Result := True;
end;

procedure TAHMRenderCache.SaveCache(Filename: String);
begin
  // Diagnostic method to allow us to see cache contents
  if Assigned(FCache) then
    FCache.SaveToFile(Filename);
end;

procedure TAHMRenderCache.Render(DestRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour;
                                 XScale, YScale: Single; XFlip, YFlip: Boolean);
begin
  // Delegate to method below
  if Assigned(FCache) then
    Render(FCache.SourceRect, DestRect, Shade, Gamma, XScale, YScale, XFlip, YFlip);
end;

procedure TAHMRenderCache.Render(SrcRect, DestRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour;
                                 XScale, YScale: Single; XFlip, YFlip: Boolean);
begin
  // First ensure that we have a cache available
  if not Assigned(FCache) then Exit;

  // Validated, ask DirectX to render our sprite for us
  DirectXInstance.DrawSprite(FCache, SrcRect, DestRect, Shade, Gamma, XScale, YScale, XFlip, YFlip);
end;

procedure TAHMRenderCache.RenderClipped(DestRect, ClipRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour;
                                        XScale, YScale: Single; XFlip, YFlip: Boolean);
begin
  // Delegate to method below
  if Assigned(FCache) then
    RenderClipped(FCache.SourceRect, DestRect, ClipRect, Shade, Gamma, XScale, YScale, XFlip, YFlip);
end;

procedure TAHMRenderCache.RenderClipped(SrcRect, DestRect, ClipRect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour;
                                        XScale, YScale: Single; XFlip, YFlip: Boolean);
begin
  // First ensure that we have a cache available
  if not Assigned(FCache) then Exit;

  // Validated, ask DirectX to render our sprite for us
  DirectXInstance.DrawSprite(FCache, SrcRect, DestRect, ClipRect, Shade, Gamma, XScale, YScale, XFlip, YFlip);
end;


end.
